﻿/*
 * Copyright (c) United Binary LLC.  All rights reserved.
 * 
 * This code is licensed under the MIT License
 * 
 * SEE: http://harnessit.codeplex.com/license
 * 
 */
using System;
using System.Collections.Generic;
using UnitedBinary.HarnessIt.ExecutionCore;
using UnitedBinary.HarnessIt.SharedTypes;
using UnitedBinary.UnitTests.Framework;
using UnitedBinary.HarnessIt.TestEngine.Local;
using UnitedBinary.UnitTests.Framework.TestItems;

namespace HarnessIt.ConsoleApp
{
	class Program
	{
		static int Main(string[] args)
		{
			try
			{
				ListHeadingText();
				if (!Validate(args))
				{
					ListUsage();
					return -900;
				}
				Assembly A = GetSelectedAssembly(args);

				TestExecutor tester = new TestExecutor(new LocalExecutionEngine());
				tester.MethodExecuted += (o, e) => ListTestOutcome(e.Method);
				tester.ClassExecuting += (o, e) => ListTestClassExecuting(e.Class);
				tester.AssemblyExecuting += (o, e) => ListTestAssemblyExecuting(e.Assembly);

				Method[] methods = tester.GetAllActiveAndRequiredMethods(new[] { A });
				tester.Run(methods);

				int failCount = CountTestFailures(methods);

				ListOutcome(failCount, methods);
			
				return failCount;
			}
			catch (Exception x)
			{
				Console.WriteLine("Error in application: " + x);
				return -911;
			}
		}

		private static void ListUsage()
		{
			Console.WriteLine("Usage:");
			Console.WriteLine();
			Console.WriteLine("You must pass a single test assembly to the application.");
			Console.WriteLine();
			Console.WriteLine("Example: HarnessItConsole.exe \"MyTestAssembly.dll\"");
			Console.WriteLine();
		}

		private static void ListTestAssemblyExecuting(IAssembly iAssembly)
		{
			Console.WriteLine("Running tests in assembly " + iAssembly.Name);
		}

		private static void ListTestClassExecuting(IClass iClass)
		{
			Console.WriteLine();
			Console.WriteLine("Running tests in class " + iClass.Name);
		}

		private static int CountTestFailures(IEnumerable<Method> methods)
		{
			int count = 0;
			foreach (Method method in methods)
			{
				if (method.TestRecord.Failures > 0)
				{
					count++;
				}
			}

			return count;
		}

		private static void ReListTestFailures(IMethod[] methods)
		{
			foreach (IMethod method in methods)
			{
				if (method.TestRecord.Failures == 0)
                {
                	continue;
                }

				Console.WriteLine("FAILURES for {0}.{1}", 
					method.ParentClass.Name, method.Name);

				for (int i = 0; i < method.TestRecord.TestObjectCount; i++)
				{
					TestObject to = method.TestRecord.GetResult(i);
					if (!(to is TestResult))
					{
						continue;
					}

					TestResult tr = (TestResult)to;
					if (tr.Success)
					{
						continue;
					}

					Console.WriteLine("   FAILED: {0}", tr.Description);
				}
			}
		}

		private static void ListTestOutcome(IMethod m)
		{
			Console.WriteLine("{0}.{1} ({2}/{3})",
							  m.ParentClass.Name, m.Name, m.TestRecord.Successes, m.TestRecord.TestCount);
			for (int i = 0; i < m.TestRecord.TestObjectCount; i++)
			{
				TestObject to = m.TestRecord.GetResult(i);
				if (to is TestResult)
				{
					TestResult tr = (TestResult)to;
					Console.WriteLine("   {0}: {1}",
									  tr.Success ? "Passed" : "FAILED",
									  tr.Description);
				}
				else
				{
					TestComment tc = (TestComment)to;
					Console.WriteLine("   Comment: " + tc.Comment);
				}
			}
		}

		private static Assembly GetSelectedAssembly(string[] args)
		{
			return new Assembly(args[0]);
		}

		private static bool Validate(string[] args)
		{
			return args.Length == 1;
		}

		private static void ListHeadingText()
		{
			Console.Title = "HarnessIt Console Runner (2.0 Beta 1)";
			Console.WriteLine("HarnessIt Console Runner - version 2.0 Beta 1");
			Console.WriteLine("Copyright 2001-2011 United Binary, LLC.");
			Console.WriteLine();
		}

		private static void ListOutcome(int failCount, IMethod[] methods)
		{
			Console.WriteLine("**********************************************");
			Console.WriteLine();

			if (failCount > 0)
			{
				Console.WriteLine("The following tests have failed:");
				Console.WriteLine();

				ReListTestFailures(methods);
				Console.WriteLine();
				Console.WriteLine("Outcome: FAILURE! There {2} {0} failed test{3} (out of {1}).",
					failCount, methods.Length, failCount == 1 ? "was":"were",
					failCount == 1 ? "" : "s");
			}
			else
			{
				Console.WriteLine("Outcome: SUCCESS! All {0} tests have passed.", methods.Length);
			}
		}
	}
}